import { nextTick, reactive, ref, unref, computed, toValue } from 'vue'
import {  ElMessage } from 'element-plus'
import { isFunction } from 'lodash-es'
import { isAsyncFunction } from '@/utils/tools'
import { useToggle } from '@vueuse/core'

/**
 * useDialogCRUD
 * @param {{
 * api: Record<string, unknown>,
 * BaseFormRef: Ref<unknown>,
 * formInfo: Record<string, unknown>,
 * mergeDialogInfo?: Record<string, unknown>,
 * customConfirmFormData?: (data: Record<string, unknown>) => Record<string, unknown>,
 * customBeforeConfirm?: (opFlag: boolean, data: Record<string, unknown>) => Record<string, unknown>,
 * customAfterConfirm?: () => void,
 * customAfterCancel?: () => void,
 * customCancel?: () => void,
 * customBeforeOpen?: (opFlag: boolean, data: Record<string, unknown>) => void,
 * customAfterOpen?: (opFlag: boolean, data: Record<string, unknown>) => void,
 * customReset?: () => void,
 * successConfirmMessage?: string
 * }} opts
 * @returns
 */
export const useDialogCRUD = (opts = {}) => {
    const {
        api,
        BaseFormRef,
        formInfo,
        mergeDialogInfo = {},
        customConfirmFormData,
        customBeforeConfirm,
        customAfterConfirm,
        customAfterCancel,
        customCancel,
        customBeforeOpen,
        customAfterOpen,
        customReset,
        successConfirmMessage = '保存成功'
    } = opts

    const opFlag = ref('')

    const [confirmLoading, toggleConfirmLoading] = useToggle(false)

    const footer = computed(() => {
        if (opFlag.value === 'view') return () => void 0
        return void 0
    })

    const dialogInfo = reactive({
        width: 800,
        modelValue: false,
        'onUpdate:modelValue': val => (dialogInfo.modelValue = val),
        header: '',
        footer,
        onConfirm: async () => {
            const valid = await unref(BaseFormRef).validate()
            if (!valid) return

            let submitFormData = {
                ...formInfo.formData
            }

            if (isFunction(customConfirmFormData)) {
                submitFormData = await customConfirmFormData(submitFormData)
            }

            if (isFunction(customBeforeConfirm)) {
                const needReturn = await customBeforeConfirm(opFlag.value, submitFormData)
                if (typeof needReturn === 'boolean' && needReturn === false) return
            }

            toggleConfirmLoading(true)
            try {
                switch (opFlag.value) {
                    case 'add':
                        await api.addData(submitFormData)
                        break
                    case 'update':
                        await api.updateData(submitFormData)
                        break
                    default:
                        break
                }
                toggleDialog(false)
                successConfirmMessage && ElMessage.success(successConfirmMessage)
                resetDialog()
                if (isFunction(customAfterConfirm)) {
                    await customAfterConfirm()
                    return
                }
            } catch (error) {
                defaultCatchError(error)
            } finally {
                setTimeout(() => {
                    toggleConfirmLoading(false)
                }, 300)
            }
        },
        onCancel: async () => {
            if (isFunction(customCancel)) {
                await customCancel()
                return
            }

            toggleDialog(false)
            resetDialog()
            if (isFunction(customAfterCancel)) {
                await customAfterCancel()
                return
            }
        },
        confirmLoading,
        ...mergeDialogInfo
    })

    const toggleDialog = flag => {
        dialogInfo.modelValue = flag
    }

    const openDialog = async (_opFlag, data) => {
        if (isAsyncFunction(customBeforeOpen)) {
            await customBeforeOpen(_opFlag, data)
        } else if (isFunction(customBeforeOpen)) {
            customBeforeOpen(_opFlag, data)
        }

        opFlag.value = _opFlag
        toggleDialog(true)
        await nextTick()

        unref(BaseFormRef).formRef.resetFields()
        const defaultFormData = toValue(formInfo.defaultFormData) || {}

        switch (opFlag.value) {
            case 'add':
                Object.assign(dialogInfo, {
                    header: '新增'
                })
                
                formInfo.formData = {
                    ...defaultFormData,
                    ...data
                }
                formInfo.disabled = false
                break
            case 'update':
                Object.assign(dialogInfo, {
                    header: '编辑'
                })

                formInfo.formData = {
                    ...data
                }
                formInfo.disabled = false
                break
            case 'view':
                Object.assign(dialogInfo, {
                    header: '查看'
                })

                formInfo.formData = {
                    ...data
                }
                formInfo.disabled = true
                break
            default:
                break
        }

        if (isFunction(customAfterOpen)) {
            customAfterOpen(_opFlag, data)
        }
    }

    const resetDialog = async () => {
        if (isFunction(customReset)) {
            await customReset()
            return
        }

        opFlag.value = ''
        unref(BaseFormRef).formRef.resetFields()
        const defaultFormData = toValue(formInfo.defaultFormData) || {}
        formInfo.formData = {
            ...defaultFormData
        }
    }

    return {
        opFlag,
        dialogInfo,
        confirmLoading,
        
        toggleDialog,
        openDialog,
        resetDialog,
        toggleConfirmLoading
    }
}

export const useDialogCRUDInstance = () => {
    const dialogRef = ref(null)

    const dialogAdd = row => {
        unref(dialogRef).openDialog('add', row)
    }

    const dialogUpdate = row => {
        unref(dialogRef).openDialog('update', row)
    }

    const dialogView = row => {
        unref(dialogRef).openDialog('view', row)
    }

    return {
        dialogRef,
        dialogAdd,
        dialogUpdate,
        dialogView
    }
}

export default useDialogCRUD